#include "document.h"
#include <QMessageBox>
#include "chooseapp.h"
#include <QtCore>
#include <QTextDocumentWriter>


//---------------------------------------------------------------------------------------------------------------
Document::Document (SessionManager *sessionManager, QObject *parent) :
    QObject(parent)
{
    m_sessionManager = sessionManager;
    m_config = new Config (this);
    m_tesseract = new Tesseract (this);

    clear ();

    connect (this,
             SIGNAL(textIsAvailable()),
             this,
             SLOT(onTextIsAvailable()));

    connect (m_tesseract,
             SIGNAL(endOfOCR(QStringList)),
             this,
             SLOT(onEndOfOCR(QStringList)));

}
//--------------------
Document::~Document ()
{
    delete m_config;
    delete m_tesseract;
}
//-----------------------------
void Document::clearSession ()
{
    m_session = & m_emptySession;
}
//------------------------------------------------------------------------------
void Document::clear()
{
    //    saveWorkSession ();
    clearSession ();
    m_documentType = TypeUnknown;
    m_pdf.clear ();
    m_originalImage.clear ();
    m_previewImage.clear ();
    setTypePdf (false);
    m_documentType = TypeUnknown;
    //    m_contrast = qRound (255.0 * 50.0 / 100.0);
    m_currentPdfPage = 0;
    m_firstPdfPage = 0;
    m_lastPdfPage = 0;
    m_pdf.setResolution (m_config->optionResolutionForPdf ());
    clearUndoAndRedo ();
    m_loadingDocument = false;

    //    emit originalPixmapAvailable (rotatedPixmap ());
    //    updatePreview ();
}
//------------------------------------------------------------------------------
bool Document::openDocument (const QString &path)
{
    Session *s = m_sessionManager->sessionOf (path);

    if (s == 0) return false;

    return openSession (s);
}

//------------------------------------------------------------------------------
bool Document::openSession (Session *session)
{
    clear ();

    if (session == 0) return false;
    if (not analyzeDocument (session->fileName ())) return false;

    m_loadingDocument = true;
    m_session = session;

    if (isPdf ()) {
        m_firstPdfPage = 1;
        m_lastPdfPage = m_pdf.pageCount ();
        m_currentPdfPage = pdfCurrentPage ();
        m_originalImage = m_pdf.pageImage (m_currentPdfPage);
    }
    else {
        m_originalImage = Image (fileName ());
    }

    if (rotation () != 0) {
        QList<QRect> savGreenRects = greenRects ();
        QList<QRect> savRedRects = redRects ();
        setAbsoluteRotation (rotation ());
        m_session->setGreenRects (savGreenRects);
        m_session->setRedRects (savRedRects);
    }
    else {
        m_rotatedImage = m_originalImage;   ///////// !!!
    }

    emit empty (isEmpty ());
    emit pathChanged (fileName ());
    emit rotationChanged (rotation ());
    emit scaleChanged (scale ());
    emit contrastChanged (contrast ());
    emit optionCleanImageChanged (mustCleanImage ());
    emit rectsChanged (redRects (), greenRects ());

    emit typePdfChanged (m_documentType == TypePdf);
    if (m_documentType == TypePdf) {
        emit pageRangeChanged (m_firstPdfPage, m_lastPdfPage);
        emit currentPageChanged (pdfCurrentPage ());
    }
    emit zoom1Changed (zoom1 ());
    emit zoom2Changed (zoom2 ());

    emit originalPixmapAvailable (rotatedPixmap ());
    emit rectsChanged (redRects (), greenRects ());

    m_loadingDocument = false;

    updatePreview ();

    return true;
}
//------------------------------------------------------------------------------
bool Document::analyzeDocument (const QString &path)
{
    // vérification du type de document
    if (path.isEmpty ()) return false;

    m_documentType = TypeUnknown;
    QFileInfo fi (path);
    if (not fi.exists ()) {
        QMessageBox::critical (0,
                               Config::appName (),
                               tr ("\nLe fichier \n%1\nn'existe pas.")
                               .arg (path));

        return false;
    }

    QString type (Config::documentLinuxType (path));

    if (type.contains ("image")) {
        m_documentType = TypeImage;
    }
    else {
        if (type.contains ("pdf") and m_pdf.load (path)) {
            m_documentType = TypePdf;
        }
    }

    if (m_documentType == TypeUnknown) {
        QMessageBox::warning (0,
                              Config::appName (),
                              tr ("\nJe ne sais pas ouvrir le fichier \n%1\n")
                              .arg (path));

        return false;
    }

    return true;
}
//--------------------------------------
bool Document::PDFDocContainsText ()
{
    return (isPdf () && m_pdf.containsText ());
}
//-----------------------------------------------------
bool Document::PDFRectContainsText(const QRectF& rect)
{
    return (isPdf () && m_pdf.textInRects (m_currentPdfPage, QVector<QRectF>() << rect, false, "").length () > m_lastPdfPage);
}
//-------------------------------------------------
void Document::exportToDicodor ()
{
    QProcess process (this);
    process.startDetached ("/data/dev/dicodor/dicodor");
    //    process.waitForFinished ();
    //    SpellChecker* dicodor = new SpellChecker ();
    //    dicodor->runAndEdit (m_text);
}
//---------------- UNDO - REDO ------------------------------------------------
void Document::undo ()
{
    if (canUndo ()) {
        prepareRedo ();
        restoreStateFrom (m_sessionManager->undoSession ());
    }
}
void Document::redo ()
{
    if (canRedo ()) {
        saveStateTo (m_sessionManager->prepareUndo ());
        restoreStateFrom (m_sessionManager->redoSession ());
    }
}

void Document::clearUndo() { m_sessionManager->clearUndo (); }

void Document::clearRedo() { m_sessionManager->clearRedo (); }

void Document::clearUndoAndRedo() { clearUndo (); clearRedo (); }

bool Document::canUndo() { return m_sessionManager->canUndo (); }

bool Document::canRedo() { return m_sessionManager->canRedo (); }

void Document::prepareUndo() { saveStateTo (m_sessionManager->prepareUndo ()); clearRedo (); }

void Document::prepareRedo() { saveStateTo (m_sessionManager->prepareRedo ()); }
//------------------------------------------------------------------------------
void Document::exportToWordProcessor ()
{
    const QString& fileName (Config::exportedTextCompleteFileName ());
    QFile file (fileName);
    if (m_config->optionExportTextToODF ()) {
        QTextDocument doc (text ());
        QTextDocumentWriter writer;
        writer.setFormat ("odf");
        writer.setFileName (fileName);
        if (not writer.write (&doc)) {
            QMessageBox::critical (0,
                                   Config::appName (),
                                   tr ("Je ne peux pas écrire dans le fichier\n%1").arg (fileName));
            return;
        }
    }
    else {
        if (not file.open (QIODevice::WriteOnly)) {
            QMessageBox::critical (0,
                                   Config::appName (),
                                   tr ("Je ne peux pas écrire dans le fichier\n%1").arg (fileName));
            return;
        }
        else {
            QTextStream out (&file);
            out.setCodec ("UTF-8");
            out << text ();
            file.close ();
        }
    }

    bool ok;

    QString commandLine = m_config->currentWordProcessorCommandLine ().arg (fileName);
    if (!commandLine.isEmpty ()) {
        // on tente l'ouverture directe par le TdT
        QProcess process;
        ok = process.startDetached (commandLine);
        process.pid (); // garder ça pour mémoire
    }
    else {
        // ouverture via xdg-open
        ok = QDesktopServices::openUrl (QUrl::fromLocalFile (fileName));
    }

    if (not ok) {
        QMessageBox::critical (0,
                               Config::appName (),
                               tr ("Le fichier d'exportation est\n%1.\nIl n'a pas pu être lu.").arg (fileName));
    }
}
//------------------------------------------------------------------------------
bool Document::restoreStateFrom (Session* session)
{
    if (session == 0) return false;

    m_session = session;
    emit rectsChanged (redRects (), greenRects ());
    updatePreview ();
    return true;
}
//------------------------------------------------------------------------------
bool Document::saveStateTo (Session *s)
{
    if (s == 0 or m_session == 0) return false;

    *s = *m_session;

    return true;
}
//------------------------------------------------------------------------------
void Document::setPath (const QString &path)
{
    m_session->setFileName (path);
}
//------------------------------------------------------------------------------
void Document::setTypePdf (const bool b)
{
    int tp = (b ? TypePdf : TypeImage);
    if (m_documentType == tp and m_session->isValid ()) return;
    m_documentType = tp;
}
//------------------------------------------------------------------------------
void Document::setOptionCleanPreview (const bool b)
{
    if (m_session->mustCleanImage () == b and m_session->isValid ()) return;
    prepareUndo ();
    m_session->setOptionCleanImage (b);
    updatePreview ();
}
//------------------------------------------------------------------------------
void Document::setDocumentType (const int type)
{
    if (type != m_documentType) {
        m_documentType = type;
    }
}
//------------------------------------------------------------------------------
void Document::setPDFPageRange (const int first, const int last)
{
    if (m_firstPdfPage == first && m_lastPdfPage == last) return;
    m_firstPdfPage = first;
    m_lastPdfPage = last;
}
//------------------------------------------------------------------------------
void Document::setPdfCurrentPage (const int pageNum)
{
    if (isPdf ()) {
        const int pageTest = qBound (m_firstPdfPage, pageNum, m_lastPdfPage);
        if (m_currentPdfPage != pageTest)
        {
            clearRectList ();
            clearUndoAndRedo ();
            m_currentPdfPage = pageTest;
            m_originalImage = m_pdf.pageImage (m_currentPdfPage);
            m_session->setRotation (0);
            emit originalPixmapAvailable (rotatedPixmap ());
            updatePreview ();
        }
    }
    else {
        if (m_currentPdfPage != 0) {
            m_currentPdfPage = 0;
            updatePreview ();
        }
    }
}
//---------------------------------------
void Document::setScale (const double s)
{
    if (s == scale () and m_session->isValid ()) return;
    prepareUndo ();
    m_session->setScale (s);
    updatePreview ();
}
//------------------------------------------------------------------------------
void Document::setContrastFromGUI (const int c)
{
    // contrast = de 0 à 100
    // m_contrast = de 0 à 255
    int newContrast = qRound (255.0 * c / 100.0);
    if (newContrast == contrast () and m_session->isValid ()) return;
    prepareUndo ();
    m_session->setContrast (newContrast);
    updatePreview ();
}
//------------------------------------------------------------------------------
void Document::setOptionCleanImage (const bool b)
{
    if (b == mustCleanImage () and m_session->isValid ()) return;
    prepareUndo ();
    m_session->setOptionCleanImage (b);
    updatePreview ();
}
//------------------------------------------------------------------------------
void Document::setRectLists (const QList<QRect>& redList, const QList<QRect>& greenList)
{
    if ((redRects () == redList and greenRects () == greenList) and m_session->isValid ()) return;
    prepareUndo ();
    m_session->setGreenRects (greenList);
    m_session->setRedRects (redList);
    updatePreview ();
}
//------------------------------------------------------------------------------
void Document::clearRectList()
{
    if (redRects ().isEmpty () and greenRects ().isEmpty ()) return;
    m_session->clearRedRects ();
    m_session->clearGreenRects ();
    emit rectsChanged (redRects (), greenRects ());
    updatePreview ();
}
//------------------------------------------------------------------------------
void Document::setAbsoluteRotation (int const angle)
{
    int result = angle % 360;
    if (result < 0) result += 360;
    // Reduction en multiple de 90
    result -= (result % 90);

//    if (result == rotation () and m_session->isValid ()) return;

    m_session->setRotation (result);
    clearRectList ();
    clearUndoAndRedo ();


    QTransform matrix;
    matrix.rotate (result);

    m_rotatedImage = m_originalImage.transformed (matrix, Qt::FastTransformation);
    m_rotatedImage.setDotsPerMeterX (m_originalImage.dotsPerMeterX ());
    m_rotatedImage.setDotsPerMeterY (m_originalImage.dotsPerMeterY ());

    emit originalPixmapAvailable (rotatedPixmap ());
    updatePreview ();
}
//---------------------------------
void Document::setRotationRight ()
{
    setAbsoluteRotation (rotation () + 90);
}
//--------------------------------
void Document::setRotationLeft ()
{
    setAbsoluteRotation (rotation () - 90);
}
//------------------------------------------------------------------------------
void Document::updatePreview ()
{
    if (m_loadingDocument) return;

    QApplication::setOverrideCursor (Qt::WaitCursor);

    const Image tempo (m_rotatedImage, *m_session);
    m_previewImage = tempo.toPreview ();
    emit previewPixmapAvailable (previewPixmap ());

    QApplication::restoreOverrideCursor ();
}
//------------------------------------------------------------------------------
void Document::onAskInformationsAboutCurrentImage ()
{
    QMessageBox::information (0,
                              tr ("Informations sur l'image"),
                              rotatedProperties ());
}
//------------------------------------------------------------------------------
void Document::onTextExtractionRequired (const QRectF &rect)
{
    setText (m_pdf.textInRects (m_currentPdfPage, QVector<QRectF>() << rect, false, QString ()));
}
//------------------------------------------------------------------------------
void Document::setText (const QString &text)
{
    static const QString caesura ("-\n");
    static const QString cr ('\n');
    static const QString space (' ');

    QString result (text);

    if (m_config->optionRemoveCaesura ()) {
        result.clear ();
        const QStringList& words (text.trimmed ().split (space, QString::SkipEmptyParts));

        foreach (const QString& word, words) {
            QString tmp (word);
            if (tmp.contains (caesura)) {
                tmp.remove (caesura);
                tmp += cr;
            }
            else {
                tmp += space;
            }
            result += tmp;
        }

        result = result.trimmed ();
    }
    if (m_config->optionReplaceCRBySpace ()) {
        result.replace (cr, space);
    }

    m_session->setText (result.trimmed ());

    if (m_session->text ().length () < 2) {
        QMessageBox::information (0,
                                  Config::appName (),
                                  tr ("Aucun texte n'a été trouvé"));
    }
    else {
        emit textIsAvailable ();
    }
}
//------------------------------------------------------------------------------
void Document::onTextExtractionRequired ()
{
    m_session->setText (QString ());


    ExtractDialog dial (m_currentPdfPage,
                        m_firstPdfPage,
                        m_lastPdfPage,
                        greenRects ().count ());

    if (dial.exec () == QDialog::Rejected) return;

    const QPair<int, int>& range (dial.range ());

    if (range.first == -1 && range.second == -1) {
        //        setText (m_pdf.textInRects (m_currentPdfPage,
        //                                    greenRectVector (),
        //                                    m_config->optionUsePdfToText (),
        //                                    fileName ()));
    }
    else {
        setText (m_pdf.textInPages (range.first,
                                    range.second,
                                    m_config->optionUsePdfToText (),
                                    fileName ()));
    }
}
//------------------------------------------------------------------------------
void Document::onOCRRequired ()
{

    QString fileName (Config::fileNameForTesseract ());
    m_previewImage.save (fileName, "TIFF", 100);
    m_tesseract->runOCROn (QStringList () << fileName);

    //    QStringList fileList;
    //    foreach (const QImage& split, splitPreview ()) {
    //        QString fileName (Config::fileNameForTesseract ());
    //        split.save (fileName, "TIFF", 100);
    //        fileList << fileName;
    //    }

    //    m_tesseract->runOCROn (fileList);
}
//------------------------------------------------------------------------------
void Document::onROIOCRRequired (ImagePart* /*part*/)
{
    //    const QList<QImage> tmp = Image::split (m_rotatedImage);
    //    QString fileName (Config::fileNameForTesseract ());
    //    tmp.at (0).save (fileName, "TIFF", 100);

    //    m_tesseract->runOCROn (QStringList () << fileName);

}
//------------------------------------------------------------------------------
void Document::onTextIsAvailable ()
{
    if (m_config->optionAskForWP ())
    {
        ChooseApp dial;
        dial.exec ();
    }
    // ... le texte est dans texte ()
    switch (m_config->optionWordProcessorIndex ())
    {
    case 0:
        exportToDicodor ();
        break;

    default:
        exportToWordProcessor ();
        break;
    }
}
//------------------------------------------------------------------------------
void Document::onEndOfOCR (const QStringList& resultNames)
{
    QString tempTxt;
    const QChar cr ('\n');
    foreach (const QString& fileName, resultNames) {
        QFile file (fileName);
        if (file.open (QIODevice::ReadOnly | QIODevice::Text))
        {
            QTextStream in (&file);
            in.setCodec ("UTF-8");
            tempTxt += in.readAll ();
            if (m_config->optionCRBetweenROI ()) {
                tempTxt += cr;
            }
        }
        else {
            qDebug () << tr ("Fichier non trouvé : %1").arg (fileName);
        }

    }
    setText (tempTxt);
}
